// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/*
 * n7 eio pin support for Lombo pinctrl driver
 *
 * Copyright(c)2018 Lombo Tech Co.Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This file contains Lombo n7 specific static information to support
 * pinctrl driver, also includes the implementation of gpio interrupt support.
 */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/syscore_ops.h>
#include <mach/core_sync.h>
#include "pinctrl-lombo.h"

struct platform_device *n7_eio_dev;

/**
 * n7 eio bank type define, contain five registers type.
 * Function configuration: 4-bits for per pin
 * Pull up or pull down configuration: 2-bits for per pin
 * Driver configuration: 3-bits for per pin
 * Data: 1-bit for per pin
 * Resistors configuration: 1-bit for per pin
 */
static const struct lombo_pin_bank_type n7_eio_bank_type = {
	.fld_width = {4, 2, 2, 1,},
	.reg_offset = {0x0, 0x10, 0x20, 0x30,},
};

#define PIN_BANK(pins, reg, id)		\
	{						\
		.type		= &n7_eio_bank_type,	\
		.pctl_offset	= reg,			\
		.npins		= pins,			\
		.eint_type	= EINT_TYPE_NONE,	\
		.name		= id			\
	}

/* n7 pin controller pin banks */
static const struct lombo_pin_bank_data n7_eio_pin_banks[] = {
	PIN_BANK(4, 0x0, "eio"),
};

/* n7 pin controller pinctrl driver data */
struct lombo_pin_ctrl n7_eio_pin_ctrl[] = {
	{
		.pin_banks	= n7_eio_pin_banks,
		.nbanks		= ARRAY_SIZE(n7_eio_pin_banks),
	},
};

static int n7_eio_pinctrl_probe(struct platform_device *pdev)
{
	n7_eio_dev = pdev;
	dev_info(&pdev->dev, "bank size: %d.\n", n7_eio_pin_ctrl[0].nbanks);

	return lombo_pinctrl_probe(pdev, n7_eio_pin_ctrl);
}

#ifdef CONFIG_PM

/**
 * save pinctrl state for suspend for a pin controller.
 *
 * Save data for all banks handled by this device.
 */
static void n7_eio_pinctrl_suspend_dev(struct lombo_pinctrl_drv_data *drv_data)
{
	void __iomem *regs_base = drv_data->regs_base;
	int i;
#ifdef CONFIG_PINCTRL_CORE_SYNC
	u32 flag;
#endif

#ifdef CONFIG_PINCTRL_CORE_SYNC
	flag = core_sync_1_start();
#endif
	for (i = 0; i < drv_data->nbanks; i++) {
		struct lombo_pin_bank *bank = &drv_data->pin_banks[i];
		void __iomem *reg = regs_base + bank->pctl_offset;
		int num;

		for (num = 0; num < BANK_REGISTERS_NUM; num++)
			bank->pm_save[num] =
				_readl_correct(reg + num * ADDR_STEP);
	}
#ifdef CONFIG_PINCTRL_CORE_SYNC
	core_sync_1_end(flag);
#endif
}

/**
 * restore pinctrl state from suspend for a pin controller.
 *
 * Restore one of the banks that was saved during suspend.
 *
 */
static void n7_eio_pinctrl_resume_dev(struct lombo_pinctrl_drv_data *drv_data)
{
	void __iomem *regs_base = drv_data->regs_base;
	int i;
#ifdef CONFIG_PINCTRL_CORE_SYNC
	u32 flag;
#endif

#ifdef CONFIG_PINCTRL_CORE_SYNC
	flag = core_sync_1_start();
#endif
	for (i = 0; i < drv_data->nbanks; i++) {
		struct lombo_pin_bank *bank = &drv_data->pin_banks[i];
		void __iomem *reg = regs_base + bank->pctl_offset;
		int num;

		for (num = 0; num < BANK_REGISTERS_NUM; num++)
			_writel_correct(bank->pm_save[num],
						reg + num * ADDR_STEP);
	}
#ifdef CONFIG_PINCTRL_CORE_SYNC
	core_sync_1_end(flag);
#endif
}

/**
 * save soc pin state for suspend, save data for all banks.
 */
static int n7_eio_pinctrl_suspend(void)
{
	struct lombo_pinctrl_drv_data *drv_data;

	drv_data = platform_get_drvdata(n7_eio_dev);
	if (drv_data)
		n7_eio_pinctrl_suspend_dev(drv_data);

	return 0;
}

/**
 * restore soc pin state for resume, restore data for all banks.
 */
static void n7_eio_pinctrl_resume(void)
{
	struct lombo_pinctrl_drv_data *drv_data;

	drv_data = platform_get_drvdata(n7_eio_dev);
	if (drv_data)
		n7_eio_pinctrl_resume_dev(drv_data);
}

#else
#define n7_eio_pinctrl_suspend		NULL
#define n7_eio_pinctrl_resume		NULL
#endif

static struct syscore_ops n7_eio_pinctrl_syscore_ops = {
	.suspend = n7_eio_pinctrl_suspend,
	.resume	 = n7_eio_pinctrl_resume,
};

static const struct of_device_id n7_eio_pinctrl_dt_match[] = {
	{ .compatible = "lombo,n7-eio-pinctrl",
		.data = n7_eio_pin_ctrl },

	{},
};
/* MODULE_DEVICE_TABLE(of, n7_eio_pinctrl_dt_match); */

static struct platform_driver n7_eio_pinctrl_driver = {
	.probe  = n7_eio_pinctrl_probe,
	.driver = {
		.name = "n7v3-eio-pinctrl",
		.of_match_table = n7_eio_pinctrl_dt_match,
	},
};

/* pinctrl driver register must at an earlier initcall level */
static int __init n7_eio_pinctrl_drv_register(void)
{
	/* register syscore ops for register save/restore */
	register_syscore_ops(&n7_eio_pinctrl_syscore_ops);

	return platform_driver_register(&n7_eio_pinctrl_driver);
}
postcore_initcall(n7_eio_pinctrl_drv_register);

static void __exit n7_eio_pinctrl_drv_unregister(void)
{
	platform_driver_unregister(&n7_eio_pinctrl_driver);
}
module_exit(n7_eio_pinctrl_drv_unregister);

MODULE_AUTHOR("lomboswer@lombotech.com");
MODULE_DESCRIPTION("Driver for LomboTech pinctrl controller");
MODULE_LICENSE("GPL");
